// SPDX-FileCopyrightText: The openTCS Authors
// SPDX-License-Identifier: MIT
package org.opentcs.access.to.peripherals;

import static java.util.Objects.requireNonNull;

import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.io.Serializable;
import java.util.Map;
import org.opentcs.access.to.CreationTO;

/**
 * A transfer object describing a peripheral job.
 */
public class PeripheralJobCreationTO
    extends
      CreationTO
    implements
      Serializable {

  /**
   * Indicates whether the name is incomplete and requires to be completed when creating the actual
   * peripheral job.
   * (How exactly this is done is decided by the kernel.)
   */
  private final boolean incompleteName;
  /**
   * A token that may be used to reserve a peripheral device.
   * A peripheral device that is reserved for a specific token can only process jobs which match
   * that reservation token.
   * This string may not be empty.
   */
  @Nonnull
  private final String reservationToken;
  /**
   * The name of the vehicle for which this peripheral job is to be created.
   */
  @Nullable
  private final String relatedVehicleName;
  /**
   * The name of the transport order for which this peripheral job is to be created.
   */
  @Nullable
  private final String relatedTransportOrderName;
  /**
   * The operation that is to be perfromed by the pripheral device.
   */
  @Nonnull
  private final PeripheralOperationCreationTO peripheralOperation;

  /**
   * Creates a new instance.
   *
   * @param name The name of this peripheral job.
   * @param reservationToken The reservation token to be used.
   * @param peripheralOperation The peripheral operation to be performed.
   */
  public PeripheralJobCreationTO(
      @Nonnull
      String name,
      @Nonnull
      String reservationToken,
      @Nonnull
      PeripheralOperationCreationTO peripheralOperation
  ) {
    super(name);
    this.incompleteName = false;
    this.reservationToken = requireNonNull(reservationToken, "reservationToken");
    this.relatedVehicleName = null;
    this.relatedTransportOrderName = null;
    this.peripheralOperation = requireNonNull(peripheralOperation, "peripheralOperation");
  }

  private PeripheralJobCreationTO(
      @Nonnull
      String name,
      @Nonnull
      Map<String, String> properties,
      boolean incompleteName,
      @Nonnull
      String reservationToken,
      @Nullable
      String relatedVehicleName,
      @Nullable
      String relatedTransportOrderName,
      @Nonnull
      PeripheralOperationCreationTO peripheralOperation
  ) {
    super(name, properties);
    this.incompleteName = incompleteName;
    this.reservationToken = requireNonNull(reservationToken, "reservationToken");
    this.relatedVehicleName = relatedVehicleName;
    this.relatedTransportOrderName = relatedTransportOrderName;
    this.peripheralOperation = requireNonNull(peripheralOperation, "peripheralOperation");
  }

  @Override
  public PeripheralJobCreationTO withName(
      @Nonnull
      String name
  ) {
    return new PeripheralJobCreationTO(
        name,
        getModifiableProperties(),
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }

  @Override
  public PeripheralJobCreationTO withProperties(
      @Nonnull
      Map<String, String> properties
  ) {
    return new PeripheralJobCreationTO(
        getName(),
        properties,
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }

  @Override
  public PeripheralJobCreationTO withProperty(
      @Nonnull
      String key,
      @Nonnull
      String value
  ) {
    return new PeripheralJobCreationTO(
        getName(),
        propertiesWith(key, value),
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }

  /**
   * Indicates whether the name is incomplete and requires to be completed when creating the actual
   * transport order.
   * (How exactly this is done is decided by the kernel.)
   *
   * @return {@code true} if, and only if, the name is incomplete and requires to be completed
   * by the kernel.
   */
  public boolean hasIncompleteName() {
    return incompleteName;
  }

  /**
   * Creates a copy of this object, with the given incomplete name flag.
   *
   * @param incompleteName The value to be set in the copy.
   * @return A copy of this object, differing in the given value.
   */
  public PeripheralJobCreationTO withIncompleteName(boolean incompleteName) {
    return new PeripheralJobCreationTO(
        getName(),
        getModifiableProperties(),
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }

  /**
   * Returns the token that may be used to reserve a peripheral device.
   *
   * @return The token that may be used to reserve a peripheral device.
   */
  public String getReservationToken() {
    return reservationToken;
  }

  /**
   * Creates a copy of this object, with the given reservation token.
   *
   * @param reservationToken The value to be set in the copy.
   * @return A copy of this object, differing in the given value.
   */
  public PeripheralJobCreationTO withReservationToken(String reservationToken) {
    return new PeripheralJobCreationTO(
        getName(),
        getModifiableProperties(),
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }

  /**
   * Returns the name of the vehicle for which this peripheral job is to be created.
   *
   * @return The name of the vehicle for which this peripheral job is to be created.
   */
  @Nullable
  public String getRelatedVehicleName() {
    return relatedVehicleName;
  }

  /**
   * Creates a copy of this object, with the given related vehicle name.
   *
   * @param relatedVehicleName The value to be set in the copy.
   * @return A copy of this object, differing in the given value.
   */
  public PeripheralJobCreationTO withRelatedVehicleName(
      @Nullable
      String relatedVehicleName
  ) {
    return new PeripheralJobCreationTO(
        getName(),
        getModifiableProperties(),
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }

  /**
   * Returns the name of the transport order for which this peripheral job is to be created.
   *
   * @return The name of the transport order for which this peripheral job is to be created.
   */
  @Nullable
  public String getRelatedTransportOrderName() {
    return relatedTransportOrderName;
  }

  /**
   * Creates a copy of this object, with the given related transport order name.
   *
   * @param relatedTransportOrderName The value to be set in the copy.
   * @return A copy of this object, differing in the given value.
   */
  public PeripheralJobCreationTO withRelatedTransportOrderName(
      @Nullable
      String relatedTransportOrderName
  ) {
    return new PeripheralJobCreationTO(
        getName(),
        getModifiableProperties(),
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }

  /**
   * Returns the operation that is to be performed by the pripheral device.
   *
   * @return The operation that is to be performed by the pripheral device.
   */
  public PeripheralOperationCreationTO getPeripheralOperation() {
    return peripheralOperation;
  }

  /**
   * Creates a copy of this object, with the given peripheral operation.
   *
   * @param peripheralOperation The value to be set in the copy.
   * @return A copy of this object, differing in the given value.
   */
  public PeripheralJobCreationTO withPeripheralOperation(
      PeripheralOperationCreationTO peripheralOperation
  ) {
    return new PeripheralJobCreationTO(
        getName(),
        getModifiableProperties(),
        incompleteName,
        reservationToken,
        relatedVehicleName,
        relatedTransportOrderName,
        peripheralOperation
    );
  }
}
